//
//  ZLLSignalReport.m
//  ZLLCrashReportDemo
//
//  Created by 张亮亮 on 2018/3/31.
//  Copyright © 2018年 张亮亮. All rights reserved.
//

#import "ZLLSignalReport.h"
#import <libkern/OsAtomic.h>
#import <execinfo.h>
#pragma Mark- 多个收集工具同时工作时

@implementation ZLLSignalReport

// #define SA_ONSTACK    0x0001    /* take signal on signal stack */
//#define SA_RESTART    0x0002    /* restart system on signal return */
//#define    SA_RESETHAND    0x0004    /* reset to SIG_DFL when taking signal */
//#define SA_NOCLDSTOP    0x0008    /* do not generate SIGCHLD on child stop */
//#define    SA_NODEFER    0x0010    /* don't mask the signal we're delivering */
//#define    SA_NOCLDWAIT    0x0020    /* don't keep zombies around */
//#define    SA_SIGINFO    0x0040    /* signal handler with SA_SIGINFO args */
//#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
//#define    SA_USERTRAMP    0x0100    /* do not bounce off kernel's sigtramp */
///* This will provide 64bit register set in a 32bit user address space */
//#define    SA_64REGSET    0x0200    /* signal handler with SA_SIGINFO args with 64bit regs information */
/*
SIGABRT--程序中止命令中止信号
SIGALRM--程序超时信号
SIGFPE--程序浮点异常信号
SIGILL--程序非法指令信号
SIGHUP--程序终端中止信号
SIGINT--程序键盘中断信号
SIGKILL--程序结束接收中止信号
SIGTERM--程序kill中止信号
SIGSTOP--程序键盘中止信号
SIGSEGV--程序无效内存中止信号
SIGBUS--程序内存字节未对齐中止信号
SIGPIPE--程序Socket发送失败中止信号
 */
/** Signal handlers that were installed before we installed ours. */
static struct sigaction* g_previousSignalHandlers = NULL;

//这只是注册了一个要注册多个 signal
bool installSignalHandler(void) {
    
    const int* fatalSignals = g_fatalSignals;
    int fatalSignalsCount = sizeof(g_fatalSignals) / sizeof(g_fatalSignals[0]);
    if (g_previousSignalHandlers == NULL) {
        g_previousSignalHandlers = malloc(sizeof(*g_previousSignalHandlers)
                                          * (unsigned)fatalSignalsCount);
    }
  
    struct sigaction action = {{0}};
    action.sa_flags = SA_SIGINFO | SA_ONSTACK;
    sigemptyset(&action.sa_mask);
    action.sa_sigaction = &ZLLSignalRegister;
    // 等同于这一句action.sa_sigaction = &ZLLSignalRegister;
    
    for (int i = 0; i < fatalSignalsCount; i++) {
       
        if (sigaction(fatalSignals[i], &action, &g_previousSignalHandlers[i]) != 0) {//失败
            for (i--; i>=0; i--) {
                sigaction(fatalSignals[i], &g_previousSignalHandlers[i], NULL);
            }
            return false;
        }
    }
    return true;
}

static void uninstallSignalHandler(void) {
    const int* fatalSignals = g_fatalSignals;
    int fatalSignalsCount = sizeof(g_fatalSignals) / sizeof(g_fatalSignals[0]);
    for(int i = 0; i < fatalSignalsCount; i++)
    {
        //把之前注册的给还回去,
        sigaction(fatalSignals[i], &g_previousSignalHandlers[i], NULL);
    }
}
//常用的
static const int g_fatalSignals[] =
{
    SIGABRT,
    SIGBUS,
    SIGFPE,
    SIGILL,
    SIGPIPE,
    SIGSEGV,
    SIGSYS,
    SIGTRAP,
};
static NSString * const SignalCrashKey = @"SignalCrashKey";
//函数名就是函数的首地址
static  void ZLLSignalRegister(int signal, siginfo_t* info, void* context) {
    //  获取堆栈，收集堆栈
    //........
    NSString * str = [NSString stringWithFormat:@"%@",@(signal)];
    [[NSUserDefaults standardUserDefaults] setObject:str forKey:SignalCrashKey];
    [[NSUserDefaults standardUserDefaults]synchronize];
    uninstallSignalHandler();
    
    
    // This is technically not allowed, but it works in OSX and iOS.  raise函数就是向当前进程发信号的。
    raise(signal);
}
+ (NSString *)currentCrashLogInfoAndClear {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *string = [defaults objectForKey:SignalCrashKey];
    [defaults setObject:nil forKey:SignalCrashKey];
    [defaults synchronize];
    return string;
}
@end



#pragma mark - 未考虑多个crash收集工具同时工作的情况
volatile int32_t UncaughtExceptionCount = 0;
const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;
const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;
const int32_t UncaughtExceptionMaximum = 10;
void SignalHandler(int signal)
{
    //iOS平台下的原子操作函数都以OSAtomic开头 不同线程如果通过原子操作函数对同一变量进行操作，可以保证一个线程的操作不会影响到其他线程内对此变量的操作，因为这些操作都是原子式的。因为原子操作只能对内置类型进行操作，所以原子操作能够同步的线程只能位于同一个进程的地址空间内。
    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    if (exceptionCount > UncaughtExceptionMaximum)
    {
        return;
    }
    //exceptionCount
    void *callstack[128];
    //获取程序的调用栈
    /* Store up to SIZE return address of the current program state in
     ARRAY and return the exact number of values stored.  */
    int frames = backtrace(callstack, 128);
    /* Return names of functions from the backtrace list in ARRAY in a newly
     malloc()ed memory block.  */
    char **strs = backtrace_symbols(callstack, frames);
    int i;
    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
    for (
         i = UncaughtExceptionHandlerSkipAddressCount;
         i < UncaughtExceptionHandlerSkipAddressCount +
         UncaughtExceptionHandlerReportAddressCount;
         i++)
    {
        [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
    }
    free(strs);
    NSLog(@"%@", backtrace);
   // return backtrace;
    
}
void InstallSignalHandler2(void) {
    signal(SIGHUP, SignalHandler);
}
